import numpy as np
import matplotlib.pyplot as plt

# -------------------------------------------------
# 1. 控制点坐标
# -------------------------------------------------
Pkm1 = np.array([0.0, 0.0])   # P_{k-1}
Pk   = np.array([1.5, 1.0])   # P_k
Pkp1 = np.array([3.0, 0.5])   # P_{k+1}

# -------------------------------------------------
# 2. TCB 参数（这里取一组示例值）
#    Tension  : -1~1   （张力）
#    Continuity: -1~1   （连续性）
#    Bias     : -1~1   （偏置）
# -------------------------------------------------
T, C, B = -0.0, -0.5, 0.0   # 这里使用默认值，便于说明概念

# -------------------------------------------------
# 3. 计算切线向量（Hermite 形式）
#    参考 Ken Perlin 的 TCB 公式
# -------------------------------------------------
def tcb_tangent(P_prev, P_curr, P_next, T, C, B, outgoing=True):
    """
    计算入切线或出切线。
    outgoing=True  → 计算 T_k^o（出切线）
    outgoing=False → 计算 T_k^i（入切线）
    """
    # 系数
    a = (1 - T) * (1 + C) * (1 + B) / 2
    b = (1 - T) * (1 - C) * (1 - B) / 2
    c = (1 - T) * (1 - C) * (1 + B) / 2
    d = (1 - T) * (1 + C) * (1 - B) / 2

    if outgoing:   # T_k^o
        return a * (P_curr - P_prev) + b * (P_next - P_curr)
    else:         # T_k^i
        return c * (P_curr - P_prev) + d * (P_next - P_curr)

Tk_i = tcb_tangent(Pkm1, Pk, Pkp1, T, C, B, outgoing=False)  # 入切线
Tk_o = tcb_tangent(Pkm1, Pk, Pkp1, T, C, B, outgoing=True)   # 出切线

# -------------------------------------------------
# 4. 生成 Hermite 曲线段（两段）
# -------------------------------------------------
def hermite_curve(P0, P1, T0, T1, n=100):
    """返回 Hermite 曲线上的点序列"""
    t = np.linspace(0, 1, n)[:, None]   # 列向量
    h00 =  2*t**3 - 3*t**2 + 1
    h10 =      t**3 - 2*t**2 + t
    h01 = -2*t**3 + 3*t**2
    h11 =      t**3 -   t**2
    return h00*P0 + h10*T0 + h01*P1 + h11*T1

# 前一段：P_{k-1} → P_k，使用 Pk 的入切线作为结束切线
curve1 = hermite_curve(Pkm1, Pk, Tk_o, Tk_i)   # 为演示，使用相同切线方向

# 当前段：P_k → P_{k+1}，使用 Pk 的出切线作为起始切线
curve2 = hermite_curve(Pk, Pkp1, Tk_o, Tk_i)   # 同上

# -------------------------------------------------
# 5. 绘图
# -------------------------------------------------
fig, ax = plt.subplots(figsize=(6, 4))
ax.set_aspect('equal')
ax.set_title(f'TCB (Tension=0, Continuity={C}, Bias=0)')

# 绘制曲线
ax.plot(curve1[:, 0], curve1[:, 1], color='steelblue', lw=2, label='incoming')
ax.plot(curve2[:, 0], curve2[:, 1], color='indianred', lw=2, label='outgoing')

# 绘制控制点
ax.scatter(*Pkm1, color='black', s=50, zorder=5)
ax.scatter(*Pk,   color='black', s=50, zorder=5)
ax.scatter(*Pkp1, color='black', s=50, zorder=5)

# 标注控制点名称
ax.text(Pkm1[0]-0.1, Pkm1[1]-0.1, r'$P_{k-1}$', fontsize=12)
ax.text(Pk[0]-0.1,   Pk[1]+0.1,   r'$P_{k}$',   fontsize=12)
ax.text(Pkp1[0]+0.05,Pkp1[1]-0.1, r'$P_{k+1}$', fontsize=12)

# 绘制切线向量（带箭头）
scale = 0.8   # 控制箭头长度的比例因子
ax.arrow(Pk[0], Pk[1],
         Tk_i[0]*scale, Tk_i[1]*scale,
         head_width=0.07, head_length=0.12,
         fc='steelblue', ec='steelblue', linewidth=1.5,
         length_includes_head=True)
ax.text(Pk[0]+Tk_i[0]*scale, Pk[1]+Tk_i[1]*scale,
        r'$T_k^{i}$', color='steelblue', fontsize=12)

ax.arrow(Pk[0], Pk[1],
         Tk_o[0]*scale, Tk_o[1]*scale,
         head_width=0.07, head_length=0.12,
         fc='indianred', ec='indianred', linewidth=1.5,
         length_includes_head=True)
ax.text(Pk[0]+Tk_o[0]*scale, Pk[1]+Tk_o[1]*scale,
        r'$T_k^{o}$', color='indianred', fontsize=12)


# 美化
ax.grid(True, linestyle='--', alpha=0.5)
ax.legend(loc='upper right')
plt.tight_layout()
plt.show()
